from typing import Callable
from vnpy.trader.constant import Interval
from vnpy.app.cta_strategy.base import StopOrderStatus
from vnpy.app.cta_strategy import (
    CtaTemplate,
    StopOrder,
    TickData,
    BarData,
    TradeData,
    OrderData,
    BarGenerator,
    ArrayManager,
)


class BollingerBotStrategy(CtaTemplate):
    """"""
    author = '用Python的交易员'

    # 策略参数
    bollLength = 32         # 通道窗口数 40
    entryDev = 2.4          # 开仓偏差 2.4
    exitDev = 1.2           # 平仓偏差 0.8
    trailingPrcnt = 0.4     # 移动止损百分比
    maLength = 30           # 过滤用均线窗口 40
    fixedSize = 1           # 每次交易的数量
    rep = 7

    # 策略变量
    bollMid = 0                         # 布林带中轨
    bollStd = 0                         # 布林带宽度
    entryUp = 0                         # 开仓上轨
    exitUp = 0                          # 平仓上轨    
    
    maFilter = 0                        # 均线过滤
    maFilter1 = 0                       # 上一期均线                   
    
    intraTradeHigh = 0                  # 持仓期内的最高点  
    longEntry = 0                       # 多头开仓
    longExit = 0                        # 多头平仓    
    
    # orderList = []                      # 保存委托代码的列表

    # 参数列表，保存了参数的名称
    parameters = ['bollLength',
                 'entryDev',
                 'exitDev',
                 'trailingPrcnt',
                 'maLength',
                 'fixedSize',
                 'rep']    

    # 变量列表，保存了变量的名称
    variables = ['bollMid',
                'bollStd',
                'entryUp',
                'exitUp',
                'intraTradeHigh',
                'longEntry',
                'longExit']  
    # variables = []

    def __init__(self, ctaEngine, strategy_name, vt_symbol, setting):
        """"""
        super(BollingerBotStrategy, self).__init__(
            ctaEngine, strategy_name, vt_symbol, setting
        )

        self.bg = BarGenerator(self.on_bar, self.rep, self.on_5min_bar, Interval.MINUTE)
        self.am = ArrayManager()

        self.buy_vt_orderids = []
        self.sell_vt_orderids = []
        self.short_vt_orderids = []
        self.cover_vt_orderids = []

        self.buy_price = 0
        self.sell_price = 0
        self.short_price = 0
        self.cover_price = 0

        self.orderList = []
        
    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("策略初始化")
        self.load_bar(10)

    def on_start(self):
        """
        Callback when strategy is started.
        """
        self.write_log("策略启动")

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        self.write_log("策略停止")

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        self.bg.update_bar(bar)
    
    def on_5min_bar(self, bar: BarData):
        """"""
        self.cancel_all()

        #信号生成    
        am = self.am
        am.update_bar(bar)
        if not am.inited:
            return
    
        self.bollMid = am.sma(self.bollLength)
        self.bollStd = am.std(self.bollLength)
        self.entryUp = self.bollMid + self.bollStd * self.entryDev
        self.exitUp = self.bollMid + self.bollStd * self.exitDev
        
        maArray = am.sma(self.maLength, array=True)
        self.maFilter = maArray[-1]
        self.maFilter1 = maArray[-2]
        
        if self.pos == 0:
            self.intraTradeHigh = bar.high_price
            
            if bar.close_price > self.maFilter and self.maFilter > self.maFilter1:
                self.longEntry = self.entryUp
                
                # self.buy_price = self.longEntry
                # self.sell_price = 0
                # self.short_price = 0
                # self.cover_price = 0
                orderID = self.buy(self.longEntry, self.fixedSize, True)
                self.orderList.extend(orderID)
    
        elif self.pos > 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high_price)
            
            self.longExit = self.intraTradeHigh * (1 - self.trailingPrcnt/100)
            self.longExit = min(self.longExit, self.exitUp)
            
            # self.buy_price = 0
            # self.sell_price = self.longExit
            # self.short_price = 0
            # self.cover_price = 0
            orderID = self.sell(self.longExit, abs(self.pos), True)
            self.orderList.extend(orderID)

        # # 根据信号执行挂撤交易
        # if self.pos == 0:
        #     # 检查之前委托都已经结束
        #     if not self.buy_vt_orderids:
        #         # 检查存在信号
        #         if self.buy_price:
        #             self.buy_vt_orderids = self.buy(self.buy_price, self.fixedSize, True, True)
        #             self.buy_price = 0      # 执行需要清空信号
        #     else:
        #         # 遍历委托号列表撤单
        #         for vt_orderid in self.buy_vt_orderids:
        #             self.cancel_order(vt_orderid)
            
        #     # if not self.short_vt_orderids:
        #     #     if self.short_price:
        #     #         self.short_vt_orderids = self.short(self.short_price, self.fixedSize, True)
        #     #         self.short_price = 0
        #     # else:
        #     #     for vt_orderid in self.short_vt_orderids:
        #     #         self.cancel_order(vt_orderid)
        # elif self.pos > 0:
        #     if not self.sell_vt_orderids:
        #         if self.sell_price:
        #             self.sell_vt_orderids = self.sell(self.sell_price, abs(self.pos), True, True)
        #             self.sell_price = 0
        #     else:
        #         for vt_orderid in self.sell_vt_orderids:
        #             self.cancel_order(vt_orderid)
        # # else:
        # #     if not self.cover_vt_orderids:
        # #         if self.cover_price:
        # #             self.cover_vt_orderids = self.cover(self.cover_price, abs(self.pos), True)
        # #             self.cover_price = 0
        # #     else:
        # #         for vt_orderid in self.cover_vt_orderids:
        # #             self.cancel_order(vt_orderid)
    
        self.put_event()        

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """
        pass

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        self.put_event()

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """
        # # 只处理撤销或者触发的停止单委托
        # if stop_order.status == StopOrderStatus.WAITING:
        #     return

        # # 移除已经结束的停止单委托号
        # for buf_orderids in [
        #     self.buy_vt_orderids,
        #     self.sell_vt_orderids,
        #     self.short_vt_orderids,
        #     self.cover_vt_orderids
        # ]:
        #     if stop_order.stop_orderid in buf_orderids:
        #         buf_orderids.remove(stop_order.stop_orderid)
        
        # # 发出新的委托
        # if self.pos == 0:
        #     if not self.buy_vt_orderids:
        #         if self.buy_price:
        #             self.buy_vt_orderids = self.buy(self.buy_price, self.fixedSize, True, True)
        #             self.buy_price = 0
            
        #     if not self.short_vt_orderids:
        #         if self.short_price:
        #             self.short_vt_orderids = self.short(self.short_price, self.fixedSize, True, True)
        #             self.short_price = 0
            
        # elif self.pos > 0:
        #     if not self.sell_vt_orderids:
        #         if not self.sell_price:
        #             self.sell_vt_orderids = self.sell(self.sell_price, abs(self.pos), True, True)
        #             self.sell_price = 0
            
        # # else:
        # #     if not self.cover_vt_orderids:
        # #         if not self.cover_price:
        # #             self.cover_vt_orderids = self.cover(self.cover_price, abs(self.pos), True)
        # #             self.cover_price = 0

        pass

class NewBarGenerator(BarGenerator):
    """"""
    def __init__(
        self,
        on_bar: Callable,
        window: int = 0,
        on_window_bar: Callable = None,
        interval: Interval = Interval.MINUTE
        ):
        super().__init__(on_bar, window, on_window_bar, interval)
    
    def update_tick(self, tick: TickData):
        """
        Update new tick data into generator.
        """
        new_minute = False

        # Filter tick data with 0 last price
        if not tick.last_price:
            return

        if not self.bar:
            new_minute = True
        # elif self.bar.datetime.minute != tick.datetime.minute:
        elif self.bar.datetime.second >= 47 and self.last_tick.datetime.second < 47:
            self.bar.datetime = self.bar.datetime.replace(
                second=0, microsecond=0
            )
            self.on_bar(self.bar)

            new_minute = True

        if new_minute:
            self.bar = BarData(
                symbol=tick.symbol,
                exchange=tick.exchange,
                interval=Interval.MINUTE,
                datetime=tick.datetime,
                gateway_name=tick.gateway_name,
                open_price=tick.last_price,
                high_price=tick.last_price,
                low_price=tick.last_price,
                close_price=tick.last_price,
                open_interest=tick.open_interest
            )
        else:
            self.bar.high_price = max(self.bar.high_price, tick.last_price)
            self.bar.low_price = min(self.bar.low_price, tick.last_price)
            self.bar.close_price = tick.last_price
            self.bar.open_interest = tick.open_interest
            self.bar.datetime = tick.datetime

        if self.last_tick:
            volume_change = tick.volume - self.last_tick.volume
            self.bar.volume += max(volume_change, 0)

        self.last_tick = tick

    def update_bar(self, bar: BarData):
        """"""
        # If not inited, creaate window bar object
        if not self.window_bar:
            # Generate timestamp for bar data
            if self.interval == Interval.MINUTE:
                dt = bar.datetime.replace(second=0, microsecond=0)
            else:
                dt = bar.datetime.replace(minute=0, second=0, microsecond=0)

            self.window_bar = BarData(
                symbol=bar.symbol,
                exchange=bar.exchange,
                datetime=dt,
                gateway_name=bar.gateway_name,
                open_price=bar.open_price,
                high_price=bar.high_price,
                low_price=bar.low_price
            )
        # Otherwise, update high/low price into window bar
        else:
            self.window_bar.high_price = max(
                self.window_bar.high_price, bar.high_price)
            self.window_bar.low_price = min(
                self.window_bar.low_price, bar.low_price)

        # Update close price/volume into window bar
        self.window_bar.close_price = bar.close_price
        self.window_bar.volume += int(bar.volume)
        self.window_bar.open_interest = bar.open_interest

        # Check if window bar completed
        finished = False

        if self.interval == Interval.MINUTE:
            # # x-minute bar
            # if not (bar.datetime.minute + 1) % self.window:
            #     finished = True
            self.interval_count += 1
            if self.last_bar and bar.datetime.minute != self.last_bar.datetime.minute:
                
                if not self.interval_count % self.window:
                    finished = True
                    self.interval_count = 0
                

        elif self.interval == Interval.HOUR:
            if self.last_bar and bar.datetime.hour != self.last_bar.datetime.hour:
                # 1-hour bar
                if self.window == 1:
                    finished = True
                # x-hour bar
                else:
                    self.interval_count += 1

                    if not self.interval_count % self.window:
                        finished = True
                        self.interval_count = 0

        if finished:
            self.on_window_bar(self.window_bar)
            self.window_bar = None

        # Cache last bar object
        self.last_bar = bar

    
        